package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.zzyl.base.ResponseResult;
import com.zzyl.constant.CacheConstants;
import com.zzyl.constant.SuperConstant;
import com.zzyl.dto.ResourceDto;
import com.zzyl.entity.Resource;
import com.zzyl.enums.BasicEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.ResourceMapper;
import com.zzyl.service.ResourceService;
import com.zzyl.utils.EmptyUtil;
import com.zzyl.utils.NoProcessing;
import com.zzyl.utils.StringUtils;
import com.zzyl.utils.UserThreadLocal;
import com.zzyl.vo.MenuMetaVo;
import com.zzyl.vo.MenuVo;
import com.zzyl.vo.TreeItemVo;
import com.zzyl.vo.TreeVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

@Service
public class ResourceServiceImpl implements ResourceService {

    @Autowired
    private ResourceMapper resourceMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;
    /**
     * 查询资源列表
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult list(ResourceDto dto) {
        String type="R";
        if (dto.getResourceType()==null){
            type="M";
        }

        //查询redis
        String listStr = redisTemplate.opsForValue().get(CacheConstants.RESOURCE_LIST+":"+type);
        //如果redis中有数据，则直接返回
        if (StringUtils.isNotEmpty(listStr)) {
            //把字符串转成集合
            List<Resource> resourceList = JSONArray.parseArray(listStr, Resource.class);
            return ResponseResult.success(resourceList);
        }
        //表示redis中没有命中
        List<Resource> list = resourceMapper.list(dto);
        //同步数据到redis中一份
        redisTemplate.opsForValue().set(CacheConstants.RESOURCE_LIST+":"+type, JSON.toJSONString(list));
        return ResponseResult.success(list);
    }

    /**
     * 查询资源列表 copy
     *
     * @param dto
     * @return
     */
    @Override
    @Cacheable(value = CacheConstants.RESOURCE_LIST,key = "#dto.hashCode()")
    public List<Resource> list1(ResourceDto dto) {
        List<Resource> list = resourceMapper.list(dto);
        return list;
    }

    /**
     * 资源树形菜单
     * @param dto,只有一个参数，label
     * @return
     */
    @Override
    @Cacheable(value = CacheConstants.RESOURCE_TREE,key = "#dto.hashCode()")
    public TreeVo tree(ResourceDto dto) {
        //1.根据条件查询菜单列表(不含按钮)
       dto = ResourceDto.builder()
               //固定的父资源编号，顶级父资源编号，截取之后的，比如父资源编号，100001000000000，截取之后，剩余100001   parent_resource_no like '100001%'
                .parentResourceNo(NoProcessing.processString(SuperConstant.ROOT_PARENT_ID))
               //指定查询的就是菜单
                .resourceType(SuperConstant.MENU)
                //指定查询状态，必须是0，启用状态
                .dataState(SuperConstant.DATA_STATE_0)
                .build();
        //目前来说，数据应该有很多条
        List<Resource> resourceList = resourceMapper.list(dto);

        //2.封装成递归格式，利用hutool工具中树形结构实现
        TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
        treeNodeConfig.setNameKey("label");//表示把默认的name字段名称改为label
        treeNodeConfig.setDeep(3);//最多展示2层
        /**
         * build(List<T> list, E rootId, TreeNodeConfig treeNodeConfig, NodeParser<T, E> nodeParser)
         * 参数1表示查询资源列表集合
         * 参数2表示顶级菜单的资源编号
         * 参数3表示 自定义属性(字段名)的配置对象
         * 参数4表示,(resource, tree)  resource名字不是固定的，但是代表了资源列表集合的遍历的泛型对象
         *  tree表示树形对象，是hutool类中提供的对象
         */
        List<Tree<String>> treeList = TreeUtil.build(resourceList, SuperConstant.ROOT_PARENT_ID, treeNodeConfig,(resource, tree) -> {
            tree.setId(resource.getResourceNo());
            tree.setName(resource.getResourceName());
            tree.setParentId(resource.getParentResourceNo());
        });
        //3.返回数据
        //类型转换
        List<TreeItemVo> children= BeanUtil.copyToList(treeList,TreeItemVo.class);

        //封装数据
        TreeItemVo itemVo = TreeItemVo.builder()
                .id(SuperConstant.ROOT_PARENT_ID)//顶级菜单的资源编号，id
                .label("智慧养老院")//自定义的顶级菜单名称，默认是根菜单
                .children(children)//展示都是顶级菜单下的所有的节点集合
                .build();

        List<TreeItemVo> items=new ArrayList();
        items.add(itemVo);

        TreeVo treeVo = TreeVo.builder().items(items).build();

        return treeVo;
    }

    /**
     * 资源添加
     *
     * @param dto
     * @return
     */
    @Caching(evict = {@CacheEvict(value = CacheConstants.RESOURCE_LIST ,allEntries = true),
            @CacheEvict(value = CacheConstants.RESOURCE_TREE ,allEntries = true)})
    @Override
    public ResponseResult addResource(ResourceDto dto) {
        //1.同步父资源的状态给子资源
            //1.1 先根据条件去查询数据，获取父资源的状态  select *from sys_resource where resource_no=#{parent_resource_no}
        Resource parentResource = resourceMapper.getResourceByParentResourceNo(dto.getParentResourceNo());

        Resource childrenResource=BeanUtil.copyProperties(dto,Resource.class);
        childrenResource.setDataState(parentResource.getDataState());
        //2.生成子资源编号 resource_no
        String childrenResourceNo=createResourceNo(childrenResource);
        childrenResource.setResourceNo(childrenResourceNo);
        //3.调用mapper实现新增资源
        resourceMapper.insertSelective(childrenResource);

        //4.返回数据
        return ResponseResult.success();
    }



    /**
     * 抽取的生成子资源编号的方法
     * @return
     */
    private String createResourceNo(Resource resource) {
        //1.如果是菜单，判断且父资源编号是否大于三级  100001
        if(resource.getResourceType().equals(SuperConstant.MENU) &&
            NoProcessing.processString(resource.getParentResourceNo()).length() /3 >=5){
            //如果大于3级，则抛出异常
            throw new BaseException(BasicEnum.RESOURCE_DEPTH_UPPER_LIMIT);
        }
        //否则，查询父资源编号下是否有下属节点
        ResourceDto dto=ResourceDto.builder()
                .parentResourceNo(resource.getParentResourceNo())
                .build();
        List<Resource> resourceList = resourceMapper.list(dto);

        //如果没有，则创建子编号
        if (EmptyUtil.isNullOrEmpty(resourceList)) {
            return NoProcessing.createNo(resource.getParentResourceNo(),false);
        }
        //如果有，先获取下属节点中的最大值编号，然后在创建子编号
        Long maxNo = resourceList.stream().map(resource1 -> {
            return Long.valueOf(resource1.getResourceNo());
        }).max(Comparator.comparing(Long::valueOf)).get();
        //返回子编号
        return NoProcessing.createNo(String.valueOf(maxNo),true);
    }


    /**
     * 根据用户id查询对应的资源数据
     *
     * @param
     * @return
     */
    @Override
    public List<MenuVo> menus() {
        //1.获取登录人信息
        Long userId = UserThreadLocal.getMgtUserId();

        //2.根据用户id查询资源数据，调用mapper
        List<MenuVo> menuVoList = resourceMapper.findListByUserId(userId);

        //3.使用hutool工具实现树形结构
        TreeNodeConfig treeNodeConfig=new TreeNodeConfig();
        treeNodeConfig.setIdKey("resourceNo");
        treeNodeConfig.setParentIdKey("parentResourceNo");

        List<Tree<String>> treeList = TreeUtil.build(menuVoList, SuperConstant.ROOT_PARENT_ID, treeNodeConfig, (menus, tree) -> {
            tree.setId(menus.getResourceNo());
            tree.setName(menus.getName());
            tree.setParentId(menus.getParentResourceNo());

            //自定义属性
            tree.putExtra("path",menus.getPath());
            tree.putExtra("redirect","/"+menus.getName());
            tree.putExtra("meta",new MenuMetaVo(menus.getName(),"",null));
        });

        //转化类型
        List<MenuVo> menuVos = BeanUtil.copyToList(treeList, MenuVo.class);

        //4.返回数据
        return menuVos;
    }
}
